﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace Truncation.Detector.Windows
{
    internal sealed class SolutionReader : ISolutionImpl
    {
        private readonly string _solutionName;
        private readonly List<ProjectImpl> _projects = new List<ProjectImpl>();
        static readonly Regex ProjectLinePattern = new Regex("^\\s*Project\\(\"(?<TypeGuid>.*)\"\\)\\s+=\\s+\"(?<Title>.*)\",\\s*\"(?<Location>.*)\",\\s*\"(?<IdGuid>.*)\"\\s*$");
        private readonly Dictionary<string, Assembly> _assemblyDictionary = new Dictionary<string, Assembly>();
        public SolutionReader(string fileName)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException("fileName");
            }
            if (!File.Exists(fileName) || !fileName.ToLowerInvariant().EndsWith(".sln"))
            {
                throw new FileNotFoundException(fileName);
            }
            // wpf 
            if (File.Exists("Truncation.Detector.WPF.dll"))
            {
                var assembly = Assembly.LoadFrom("Truncation.Detector.WPF.dll");
                _assemblyDictionary.Add("wpf", assembly);
            }
            if (File.Exists("Truncation.Detector.WinForm.dll"))
            {
                var assembly = Assembly.LoadFrom("Truncation.Detector.WinForm.dll");
                _assemblyDictionary.Add("winform", assembly);
            }
            if (File.Exists("Truncation.Detector.WinPhone.dll"))
            {
                var assembly = Assembly.LoadFrom("Truncation.Detector.WinPhone.dll");
                _assemblyDictionary.Add("winphone", assembly);
            }
            if (File.Exists("Truncation.Detector.Win32.dll"))
            {
                var assembly = Assembly.LoadFrom("Truncation.Detector.Win32.dll");
                _assemblyDictionary.Add("win32", assembly);
            }
            string folder = Path.GetDirectoryName(fileName);
            if (folder == null)
            {
                throw new FileNotFoundException(fileName);
            }
            _solutionName = Path.GetFileNameWithoutExtension(fileName);
            foreach (var line in File.ReadLines(fileName, Encoding.UTF8))
            {
                Match match = ProjectLinePattern.Match(line);
                if (!match.Success)
                    continue;
                Guid guid;
                Guid.TryParse(match.Groups["TypeGuid"].Value, out guid);
                var info = new ProjectInfo
                {
                    Title = match.Groups["Title"].Value,
                    Location = Path.Combine(folder, match.Groups["Location"].Value),
                    Guid = guid
                };
                CheckProjectInfo(info);
            }
        }

        public IEnumerable<ProjectImpl> ProjectCollection { get { return _projects; } }

        private void CheckProjectInfo(ProjectInfo projectInfo)
        {
            ProjectImpl dotProject = null;
            if (projectInfo.Guid == ProjectTypeGuids.CSharp && File.Exists(projectInfo.Location))
            {
                var project = new Microsoft.Build.Evaluation.Project(projectInfo.Location);
                string outputType = project.GetPropertyValue("OutputType");
                if (!String.IsNullOrEmpty(outputType) && outputType.ToLowerInvariant() == "console")
                    return;
                string typeGuids = project.GetPropertyValue("ProjectTypeGuids");
                var referents = project.GetItems("Reference");
                bool isWinform = false;
                bool isWpf = false;
                foreach (var item in referents)
                {
                    if (item.EvaluatedInclude.Equals("System.Windows.Forms", StringComparison.OrdinalIgnoreCase))
                    {
                        isWinform = true;
                    }
                    if (item.EvaluatedInclude.Equals("System.Xaml", StringComparison.OrdinalIgnoreCase))
                    {
                        isWpf = true;
                    }
                }
                Guid guid = Guid.Empty;
                
                if (!String.IsNullOrEmpty(typeGuids))
                {
                    string[] types = typeGuids.Split(';');
                    foreach (var t in types)
                    {
                        Guid.TryParse(t, out guid);
                        if (!guid.Equals(ProjectTypeGuids.CSharp))
                        {
                            break;
                        }
                    }

                }
                if (String.IsNullOrEmpty(outputType))
                {

                }
                else
                {
                    if (outputType.ToLowerInvariant() == "winexe")
                    {
                        if (guid == Guid.Empty && isWinform)
                        {
                            // winform
                            dotProject = WinformInstance(project);
                        }
                        else if (guid == ProjectTypeGuids.WPF && isWpf)
                        {
                            // wpf
                            dotProject = WpfInstance(project);
                        }
                        else
                        {
                            throw new Exception();
                        }
                    }
                    else if (outputType.ToLowerInvariant() == "library")
                    {
                        if (guid == Guid.Empty && isWinform)
                        {
                            // winform
                            dotProject = WinformInstance(project);
                        }
                        else if (guid == ProjectTypeGuids.WPF && isWpf)
                        {
                            // wpf
                            dotProject = WpfInstance(project);
                        }
                        else if (guid == ProjectTypeGuids.WinPhoneSilverLight)
                        {
                            // winphone
                        }
                        else if (guid == ProjectTypeGuids.WinPhoneRT)
                        {
                            // win store app
                        }
                        else
                        {
                            throw new Exception();
                        }
                    }
                    else if (outputType.ToLowerInvariant() == "exe")
                    {
                        // console
                    }
                    else if (outputType.ToLowerInvariant() == "appcontainerexe")
                    {
                        if (guid == ProjectTypeGuids.WinPhoneRT)
                        {
                            // win store app
                        }
                    }
                }
            }
            else if (projectInfo.Guid == ProjectTypeGuids.CPlusPlus && File.Exists(projectInfo.Location))
            {
                var project = new Microsoft.Build.Evaluation.Project(projectInfo.Location);
                project.SetGlobalProperty("Configuration", "Release");
                project.SetGlobalProperty("Platform", "Win32");
                var isWin32 = project.GetPropertyValue("Keyword");

                if (!String.IsNullOrEmpty(isWin32) &&
                    (isWin32.ToLowerInvariant().Equals("win32proj") || isWin32.ToLowerInvariant().Equals("mfcproj") || isWin32.ToLowerInvariant().Equals("managedcproj")))
                {
                    var links = project.ItemDefinitions["Link"];
                    var subsystem = links.GetMetadataValue("SubSystem");
                    var culture = project.ItemDefinitions["ResourceCompile"];
                    if (!String.IsNullOrEmpty(subsystem) && subsystem.ToLowerInvariant().Equals("windows"))
                        dotProject = Win32Instance(project);
                }
            }
            if (dotProject != null)
                _projects.Add(dotProject);
        }

        public ProjectImpl GetProject(string assemblyName)
        {
            return ProjectCollection.FirstOrDefault(projectImpl => projectImpl.AssemblyName == assemblyName);
        }

        private ProjectImpl WinformInstance(Microsoft.Build.Evaluation.Project project)
        {
            if (_assemblyDictionary.ContainsKey("winform"))
            {
                var type = _assemblyDictionary["winform"].GetType("Truncation.Detector.WinForm.Entry");
                return
                    (ProjectImpl)Activator.CreateInstance(type, new object[] { project });
            }
            return null;
        }

        private ProjectImpl WpfInstance(Microsoft.Build.Evaluation.Project project)
        {
            if (_assemblyDictionary.ContainsKey("wpf"))
            {
                var type = _assemblyDictionary["wpf"].GetType("Truncation.Detector.WPF.Entry");
                return
                    (ProjectImpl)Activator.CreateInstance(type, new object[] { project });
            }
            return null;
        }

        private ProjectImpl Win32Instance(Microsoft.Build.Evaluation.Project project)
        {
            if (_assemblyDictionary.ContainsKey("win32"))
            {
                var type = _assemblyDictionary["win32"].GetType("Truncation.Detector.Win32.Entry");
                return
                    (ProjectImpl)Activator.CreateInstance(type, new object[] { project });
            }
            return null;
        }
    }



    /// <summary>
    /// Contains constants for the project type GUIDs.
    /// 
    /// Source: http://www.mztools.com/Articles/2008/MZ2008017.aspx
    /// </summary>
    public static class ProjectTypeGuids
    {
        public static readonly Guid SolutionFolder = Guid.Parse("{2150E333-8FDC-42A3-9474-1A3956D46DE8}");

        public static readonly Guid CSharp = Guid.Parse("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}");
        public static readonly Guid VB = Guid.Parse("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}");
        public static readonly Guid CPlusPlus = Guid.Parse("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}");
        public static readonly Guid WPF = Guid.Parse("{60DC8134-EBA5-43B8-BCC9-BB4BC16C2548}");
        public static readonly Guid WinPhoneSilverLight = Guid.Parse("{C089C8C0-30E0-4E22-80C0-CE093F111A43}");
        public static readonly Guid WinPhoneRT = Guid.Parse("{76F1466A-8B6D-4E39-A767-685A06062A39}");
        public static readonly Guid WebApplication = Guid.Parse("{349C5851-65DF-11DA-9384-00065B846F21}");
        public static readonly Guid WebSite = Guid.Parse("{E24C65DC-7377-472B-9ABA-BC803B73C61A}");
        public static readonly Guid Silverlight = Guid.Parse("{A1591282-1198-4647-A2B1-27E5FF5F6F3B}");
        public static readonly Guid PortableLibrary = Guid.Parse("{786C830F-07A1-408B-BD7F-6EE04809D6DB}");
    }

    internal static class TruncationPlugin
    {
        internal static readonly string WinPhone = "Truncation.Detector.WinPhone";
        internal static readonly string WinForm = "Truncation.Detector.WinForm";
        internal static readonly string WPF = "Truncation.Detector.WPF";
    }
}
